use backyard;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::{mpsc, Arc, Mutex};
use std::time::Duration;
use std::{thread, vec};

fn main() {
    let mut tup: (u32, u64, std::string::String) = (1, 2, String::from("hello"));
    println!("{}, {}, {}", tup.0, tup.1, tup.2);

    let (ref mut a, ref mut b, ref mut c) = tup;
    println!("{}, {}, {}", a, b, c);
    *a = 10;
    *b = 20;
    println!("{}, {}, {}", a, b, c);
    println!("{:?}", tup);
    let mut arr = [1, 2, 3, 4, 5, 6];
    arr[0] = 5;
    println!("{:?}", arr);

    another_function(arr[0]);
    println!("{}", arr[0]);
    println!("{:?}", another_function(arr[0]));

    match arr[0] % 5 {
        0 => println!("divisible by 5"),
        1 => println!("divisible by 3"),
        2 => println!("divisible by 2"),
        3 => println!("divisible by 1"),
        4 => println!("divisible by 4"),
        _ => println!("divisible by 6"),
    }

    let mut count = 0;
    'count_break: loop {
        let mut remain = 10;

        loop {
            if remain == 5 {
                break;
            }
            if count == 2 {
                break 'count_break;
            }
            remain -= 1;
        }
        count += 1;
    }
    println!("count = {}", count);
    let mut index = 0;
    while index < arr.len() {
        println!("arr[{}] = {}", index, arr[index]);
        index += 1;
    }
    for elem in arr {
        println!("{}", elem);
    }

    for i in (0..arr.len()).rev() {
        println!("arr[{}] = {}", i, arr[i]);
    }

    ownership();
    let mut usr1 = struct_test();
    let usr2 = User {
        email: String::from("someone@example.com"),
        ..usr1
    };
    println!("{:?}", usr2);

    enum_test();

    collection_test();

    tait_test();

    lifecycle_test();

    closure_test();

    backyard::eat_at_restaurant();

    pointer_test();

    rc_test();

    refcell_test();

    channel_test();

    mutex_test();
}

fn another_function(x: i32) -> (i32, i32) {
    println!("The value of x is: {}", x);
    (x + 1, x + 2)
}

fn ownership() {
    let mut str1 = String::from("hello");

    str1.push_str(" world");

    let str2 = &mut str1;
    str2.push_str(" rust");
    println!("The value of str is: {}", str2);
    my_print(str2);
    str2.push_str("123");
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    let r3 = &mut s;
    let r4 = &mut s;
    println!("{} and {}", r4, r4);
}

fn my_print(s: &mut String) {
    println!("{}", s);
}

#[derive(Debug)]
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
}

impl User {
    fn get_sign_in_count(&self) -> u64 {
        self.sign_in_count
    }
    fn set_sign_in_count(&mut self, sign_in_count: u64) {
        self.sign_in_count = sign_in_count;
    }
}

#[derive(Debug)]
struct Color(i32, i32, i32);
struct AlwaysEqual;
fn struct_test() -> User {
    let mut c = Color(0, 0, 0);
    c.0 = 200;
    let mut b = (0, 0, 0);
    b.0 = 200;
    println!("{:?} {:?}", c, b);
    let d = AlwaysEqual {};
    let mut u = User {
        username: String::from("test"),
        email: String::from("test@cc.com"),
        sign_in_count: 1,
    };
    u.set_sign_in_count(100);
    println!("{}", u.get_sign_in_count());
    u
}

#[derive(Debug)]
enum IpKind {
    V4(String),
    V6(String),
}

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("Quit"),
            Message::Move { x, y } => println!("Move {} {}", x, y),
            Message::Write(s) => println!("Write {}", s),
            Message::ChangeColor(r, g, b) => println!("ChangeColor {} {} {}", r, g, b),
        }
    }
}

fn enum_test() {
    let ip1 = IpKind::V4(String::from("127.0.0.1"));
    let ip2 = IpKind::V6(String::from("::1"));
    dbg!(ip1);
    dbg!(ip2);

    let m = Message::Write(String::from("hello"));
    m.call();

    let some_number = Some(5);
    let some_string = Some("a string");
    let absent_number: Option<u32> = None;
}

#[derive(Debug)]
enum SpreadsheetCell {
    Int(i32),
    Float(f64),
    Text(String),
}

fn collection_test() {
    let mut vector = vec![1, 2, 3];
    let third: &i32 = &vector[2];

    println!("The third element is {}", third);
    vector.push(4);

    for i in &mut vector {
        *i += 50;
    }

    for i in &vector {
        println!("{}", i);
    }

    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];

    for i in &row {
        println!("{:?}", i);
    }

    let s1 = String::from("hello");
    let s2 = String::from("world");
    let s3 = s1 + &s2;
    println!("{s3}");

    let mut map = HashMap::new();

    map.insert("abc", 123);
    map.insert("def", 456);

    map.entry("abc").or_insert(789);
    for (k, v) in &mut map {
        *v += 2;
    }
    for (k, v) in &map {
        println!("{}: {}", k, v);
    }
}

trait Summary {
    fn summarize(&self) -> String;
}

trait Display {
    fn display(&self) -> String {
        "display".to_string()
    }
}

struct Weibo {
    pub name: String,
}

impl Summary for Weibo {
    fn summarize(&self) -> String {
        format!("Weibo {}", self.name)
    }
}

fn trait_call1(summary: &(impl Summary)) {
    println!("{}", summary.summarize());
}

fn trait_call2<T: Summary>(summary: &T) {
    println!("{}", summary.summarize());
}

fn trait_call3<T>(summary: &T)
where
    T: Summary,
{
    println!("{}", summary.summarize());
}

fn tait_test() {
    let wb = Weibo {
        name: String::from("hello"),
    };

    trait_call1(&wb);
    trait_call2(&wb);
    trait_call3(&wb);

    let s1: str = "Hello there!";
    let s2: str = "How's it going?";
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn lifecycle_test() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {result}");
}

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
fn closure_test() {
    let mut list = [
        Rectangle {
            width: 10,
            height: 1,
        },
        Rectangle {
            width: 3,
            height: 5,
        },
        Rectangle {
            width: 7,
            height: 12,
        },
    ];
    let mut num_of_op = 0;

    list.sort_by_key(|r| {
        num_of_op += 1;
        r.width
    });

    let res = list
        .into_iter()
        .filter(|r| r.width > 3)
        .collect::<Vec<Rectangle>>();
    println!("{:?}", res);
}

#[derive(Debug)]
enum List {
    Cons(i32, Box<List>),
    Nil,
}

struct MyBox<T: Debug>(T);

impl<T: Debug> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

impl<T: Debug> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T: Debug> Drop for MyBox<T> {
    fn drop(&mut self) {
        println!("Dropping MyBox {:#?}", self.0);
    }
}

struct MyBox2<T>(T);

impl<T> MyBox2<T> {
    fn new(x: T) -> MyBox2<T> {
        MyBox2(x)
    }
}

fn pointer_test() {
    let t = Box::new(5);
    println!("{}", t);

    use List::{Cons, Nil};
    let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
    println!("{:?}", list);

    let x = 5;
    let y = Box::new(5);
    assert_eq!(5, x);
    assert_eq!(5, *y);

    let my_box = MyBox::new(String::from("Rust"));
    assert_eq!("Rust", *my_box.deref());

    fn test_int(n: i32) {}
    test_int(*y);
    fn test_str(str: &str) -> bool {
        "Rust" == String::from("Rust")
    }
    println!("{:?}", test_str(&my_box));
}

#[derive(Debug)]
enum RcList {
    Cons(i32, Rc<crate::RcList>),
    Nil,
}

fn rc_test() {
    let a = Rc::new(crate::RcList::Cons(10, Rc::new(crate::RcList::Nil)));
    Rc::downgrade(&a);
    println!("rc count = {}", Rc::strong_count(&a));
    {
        let b = RcList::Cons(3, Rc::clone(&a));
        println!("rc count = {}", Rc::strong_count(&a));
        {
            let c = RcList::Cons(4, Rc::clone(&a));
            if let RcList::Cons(m, n) = c {
                println!("{m} {n:?}");
                println!("rc count = {}", Rc::strong_count(&a));
            }
            println!("rc count = {}", Rc::strong_count(&a));
        }
        println!("rc count = {}", Rc::strong_count(&a));
    }
    println!("rc count = {}", Rc::strong_count(&a));
}

pub trait Messenger {
    fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T: Messenger> {
    messenger: &'a T,
    value: usize,
    max: usize,
}

impl<'a, T> LimitTracker<'a, T>
where
    T: Messenger,
{
    pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
        LimitTracker {
            messenger,
            value: 0,
            max,
        }
    }

    pub fn set_value(&mut self, value: usize) {
        self.value = value;

        let percentage_of_max = self.value as f64 / self.max as f64;

        if percentage_of_max >= 1.0 {
            self.messenger.send("Error: You are over your quota!");
        } else if percentage_of_max >= 0.9 {
            self.messenger
                .send("Urgent warning: You've used up over 90% of your quota!");
        } else if percentage_of_max >= 0.75 {
            self.messenger
                .send("Warning: You've used up over 75% of your quota!");
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;

    struct MockMessenger {
        sent_messages: RefCell<Vec<String>>,
    }

    impl MockMessenger {
        fn new() -> MockMessenger {
            MockMessenger {
                sent_messages: RefCell::new(vec![]),
            }
        }
    }

    impl Messenger for MockMessenger {
        fn send(&self, message: &str) {
            let mut one_borrow = self.sent_messages.borrow_mut();
            one_borrow.push(String::from(message));
        }
    }

    #[test]
    fn it_sends_an_over_75_percent_warning_message() {
        let mock_messenger = MockMessenger::new();
        let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);

        limit_tracker.set_value(80);

        assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
    }
}
fn refcell_test() {
    let a = vec![1, 2, 3];
    let b = RefCell::new(a);

    b.borrow_mut().push(4);
    println!("a = {:?}", b.borrow());
}

fn channel_test() {
    let (tx, rx) = mpsc::channel();

    let tx1 = tx.clone();
    thread::spawn(move || {
        let messages = vec![
            String::from("hi"),
            String::from("from"),
            String::from("test"),
        ];
        for msg in messages {
            thread::sleep(Duration::from_millis(100));
            tx1.send(msg).unwrap();
        }
    });

    thread::spawn(move || {
        let messages = vec![
            String::from("hi 1"),
            String::from("from 1"),
            String::from("test 1"),
        ];
        for msg in messages {
            thread::sleep(Duration::from_millis(100));
            tx.send(msg).unwrap();
        }
    });

    for msg in rx {
        println!("Got: {}", msg);
    }
}

fn mutex_test() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();

            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}
